package chen.nf.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ServiceLoader;

/**
 * @Author czl
 * @Date 2024-05-10
 */
public class BeanProcessor {
    /**
     * 基于SPI加载所有TypeHandler实现
     */
    private static final ServiceLoader<TypeConverter> TYPE_HANDLERS = ServiceLoader.load(TypeConverter.class);

    /**
     * 根据结果集创建Bean实例
     */
    public static <T> T createBean(ResultSet rs, Class<T> beanClass) throws SQLException {
        //创建Bean实例
        T instance = newInstance(beanClass);
        //获取Bean的属性描述器
        PropertyDescriptor[] pds = propertyDescriptors(beanClass);
        //循环遍历结果集MetaData的columnCount
        for(int i=1; i<=rs.getMetaData().getColumnCount(); i++) {
            String columnLabel = rs.getMetaData().getColumnLabel(i);
            //循环所有的PropertyDescriptor（属性描述符）
            for(PropertyDescriptor pd : pds) {
                //解析PropertyDescriptor是否匹配映射的字段，如果匹配则执行映射
                if(hasColumnLabel(columnLabel, pd, beanClass)) {
                    //获取rs的value
                    Object value = processColumn(pd, columnLabel, rs);
                    //如果字段是基本类型并且赋值内容为null，这样直接赋值会报错
                    //因此当值为null时，基本数据类型的字段不做任何处理使用默认值即可
                    if(pd.getPropertyType().isPrimitive() && value == null) {
                        break;
                    }
                    //调用set方法赋值
                    callSetter(pd, instance, value);
                    break;
                }
            }
        }
        return instance;
    }

    /**
     * 根据Class对象创建实例
     */
    private static <T> T newInstance(Class<T> beanClass) throws SQLException{
        try {
            return beanClass.getConstructor().newInstance();
        } catch (Exception e) {
            throw new SQLException("Cannot create " + beanClass.getName() + ": " + e.getMessage());
        }
    }

    /**
     * 通过内省得到所有的属性描述器
     * @param beanClass bean的Class对象
     * @return 属性描述器数组
     * @throws SQLException sql异常
     */
    private static PropertyDescriptor[] propertyDescriptors(Class<?> beanClass) throws SQLException{
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(beanClass, Object.class);
            return beanInfo.getPropertyDescriptors();
        } catch (IntrospectionException e) {
            throw new SQLException("Bean introspection fail: " + e.getMessage());
        }
    }

    /**
     * 根据属性描述器解析是否存在此列名
     * @param pd 属性描述器
     * @param beanClass bean的Class对象
     * @return true表示存在此列，false则不存在
     * @throws SQLException sql异常
     */
    private static boolean hasColumnLabel(String columnLabel, PropertyDescriptor pd, Class<?> beanClass) throws SQLException{
        try {
            //获取字段的名称
            String fieldName = pd.getName();
            //根据字段名获取Field对象
            Field field = beanClass.getDeclaredField(fieldName);
            //获取字段上的注解,如果声明了注解，则使用该注解的value作为列名
            if(field.isAnnotationPresent(Column.class)) {
                fieldName = field.getAnnotation(Column.class).value();
            }
            return fieldName.equalsIgnoreCase(columnLabel);
        } catch (NoSuchFieldException e) {
            throw new SQLException("Cannot resolve " + pd.getName() + ": " + e.getMessage());
        }
    }

    /**
     * 根据字段类型获取rs的值
     * @param pd 属性描述器
     * @param columnLabel 列的标签
     * @param rs 结果集
     * @return Object
     * @throws SQLException sql异常
     */
    private static Object processColumn(PropertyDescriptor pd, String columnLabel,
                                        ResultSet rs) throws SQLException{
        try{
            Class<?> fieldType = pd.getPropertyType();
            Object value = rs.getObject(columnLabel);
            //如果是需要转换处理的类型，则根据字段类型使用相应的TypeHandler进行转换处理
            for(TypeConverter typeConverter : TYPE_HANDLERS) {
                if(typeConverter.supports(fieldType)) {
                    value = typeConverter.apply(fieldType, value);
                    break;
                }
            }
            return value;
        }catch(Exception e) {
            throw new SQLException("Process column " + pd.getName() + " fail : " + e.getMessage());
        }
    }

    /**
     * 调用描述器的set方法进行赋值操作
     * @param pd 属性描述器
     * @param beanInstance bean的实例
     * @param value 赋值参数
     */
    private static void callSetter(PropertyDescriptor pd, Object beanInstance,
                                   Object value) throws SQLException {
        try{
            //最后通过调用bean的set方法将value赋值进去
            pd.getWriteMethod().invoke(beanInstance, value);
        }catch(Exception e) {
            throw new SQLException("Cannot set " + pd.getName() + ": " + e.getMessage());
        }
    }
}
